package cn.wind.xboot.core.exception;


import cn.wind.xboot.core.res.ApiException;
import cn.wind.xboot.core.res.ApiResult;
import cn.wind.xboot.core.res.ApiStatus;
import cn.wind.xboot.core.utils.JsonUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Map;
import java.util.Set;

/**
 * @author xukk
 * @date 2018/5/4
 */
@Slf4j
@ControllerAdvice
public class ExceptionAdvice {

    @ExceptionHandler(value = {ConstraintViolationException.class})
    public ResponseEntity handleResourceNotFoundException(HttpServletRequest request, ConstraintViolationException e) {
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        StringBuilder strBuilder = new StringBuilder();
        for (ConstraintViolation<?> violation : violations) {
            strBuilder.append(violation.getMessage());
            break;
        }
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(ApiResult.Custom()
                        .failure(HttpStatus.BAD_REQUEST.value(),
                                ConstraintViolationException.class.getSimpleName(),
                                strBuilder.toString())
                        .path(request.getServletPath()));
    }

    @ExceptionHandler(value = {ValidationException.class})
    public ResponseEntity handleResourceNotFoundException(HttpServletRequest request, ValidationException e) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(ApiResult.Custom().
                        failure(HttpStatus.BAD_REQUEST.value(),
                                ConstraintViolationException.class.getSimpleName(),
                                e.getMessage())
                        .path(request.getServletPath()));
    }

    @ExceptionHandler(BindException.class)
    public ResponseEntity handleBindException(HttpServletRequest request, BindException e) {
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();
        String field = error.getField();
        String message = String.format("%s:%s", field, error.getDefaultMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(ApiResult.Custom().failure(HttpStatus.BAD_REQUEST.value(), BindException.class.getSimpleName(), message).path(request.getServletPath()));
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseEntity handleMissingServletRequestParameterException(HttpServletRequest request, MissingServletRequestParameterException e) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(ApiResult.Custom().failure(HttpStatus.BAD_REQUEST.value(), BindException.class.getSimpleName(), e.getMessage()).path(request.getServletPath()));
    }

    @ExceptionHandler(javax.validation.ValidationException.class)
    public ResponseEntity handleValidationException(HttpServletRequest request,javax.validation.ValidationException e) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(ApiResult.Custom().failure(HttpStatus.BAD_REQUEST.value(), ValidationException.class.getSimpleName(), e.getMessage()).path(request.getServletPath()));
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity handleHttpMessageNotReadableException(HttpServletRequest request,HttpMessageNotReadableException e) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(ApiResult.Custom().failure(HttpStatus.BAD_REQUEST.value(), HttpMessageNotReadableException.class.getSimpleName(), e.getMessage()).path(request.getServletPath()));
    }

    /**
     * 405 - Method Not Allowed
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity handleHttpRequestMethodNotSupportedException(HttpServletRequest request, HttpServletResponse response, HttpRequestMethodNotSupportedException e) {
        return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED)
                .body(ApiResult.Custom().failure(HttpStatus.METHOD_NOT_ALLOWED.value(), HttpRequestMethodNotSupportedException.class.getSimpleName(), e.getMessage()).path(request.getServletPath()));
    }

    /**
     * 415 - Unsupported Media Type
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @SneakyThrows
    public ResponseEntity handleHttpMediaTypeNotSupportedException(HttpServletRequest request, HttpMediaTypeNotSupportedException e) {
        return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
                .body(ApiResult.Custom().failure(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), HttpMediaTypeNotSupportedException.class.getSimpleName(), e.getMessage()).path(request.getServletPath()));
    }

    /**
     * 404 - Not Found
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity handleNoHandlerFoundException(HttpServletRequest request, NoHandlerFoundException e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(ApiResult.Custom().failure(HttpStatus.NOT_FOUND.value(), NoHandlerFoundException.class.getSimpleName(), e.getMessage()).path(request.getServletPath()));
    }

    /**
     * 500 - Internal Server Error
     */
    @ExceptionHandler(NullPointerException.class)
    public ResponseEntity handleNullPointerException(HttpServletRequest request, NullPointerException e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(ApiResult.Custom().
                        failure(HttpStatus.INTERNAL_SERVER_ERROR.value(),
                                NullPointerException.class.getSimpleName(), e.getMessage()).path(request.getServletPath()));
    }

    @ExceptionHandler(value = {RestClientResponseException.class})
    public ResponseEntity restClientResponse(RestClientResponseException e) {
        return ResponseEntity.status(e.getRawStatusCode()).body(JsonUtil.toMap(e.getResponseBodyAsString(), Map.class));
    }

    @ExceptionHandler(value = {ApiException.class})
    public ResponseEntity apiException(HttpServletRequest request,ApiException e) {
        Integer code = e.getCode();
        if (code == null) {
            code = ApiStatus.FAIL.code();
        }
        return ResponseEntity.status(200).body(ApiResult.Custom()
                .failure(e.getCode() == null ? code : e.getCode(), e.getMessage()).
                        path(request.getServletPath()));
    }


}
